﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Xunit;
using M3G;

namespace MyTestProject {
    public class TestPickGroup {
        #region Field
        Mesh msh;
        #endregion

        /**
         * 交差判定に使うメッシュ(板ポリ)の作成。
         * メッシュ(-2,2)の方がバウンディングボリューム(-1,1)(BV)より大きい事に注意する。
         * 判定はまずBVで実行され次にメッシュで実行される。
         * 従ってもしメッシュで判定すればヒットするとしてもBVで除外される事はあり得る。
         * これは意図した動作である。
         * */
        public TestPickGroup () {
            msh = new Mesh (1, 0);
            var vbuf = new VertexBuffer ();
            var positions  = new VertexArray (4, 3, ArrayType.Float);
            var normals    = new VertexArray (4, 3, ArrayType.Float);
            var texCoords1 = new VertexArray (4, 2, ArrayType.Float);
            var texCoords2 = new VertexArray (4, 2, ArrayType.Float);
            positions.Set (0, 4, new float[] { 2, -2, 0,
                                               2,  2, 0,
                                              -2, -2, 0,
                                              -2,  2, 0 });
            normals.Set (0, 4, new float[] { 0, 0, 1,
                                             0, 0, 1,
                                             0, 0, 1,
                                             0, 0, 1});
            texCoords1.Set (0, 4, new float[] { 1, 0,
                                                1, 1,
                                                0, 0,
                                                0, 1});
            texCoords2.Set (0, 4, new float[] { 1, 1,
                                                1, 1,
                                                1, 1,
                                                1, 1});
            vbuf.SetPositions (positions, 1, new float[] { 0, 0, 0 });
            vbuf.SetNormals (normals);
            vbuf.SetTexCoords (0, texCoords1, 1, new float[] { 0, 0 });
            vbuf.SetTexCoords (1, texCoords2, 1, new float[] { 0, 0 });

            var stripLengths = new int[] { 4 };
            var indices = new int[] { 0, 1, 2, 3 };
            var ibuf = new IndexBuffer (PrimitiveType.Triangles, stripLengths, indices);

            msh.SetVertexBuffer (vbuf);
            msh.SetIndexBuffer (0, ibuf);
            msh.SetBoundingSphere (0, 0, 0, 1);
            msh.SetBoundingBox (-1, 1, -1, 1, -1, 1);   // minX, maxX, ...

        }

        /**
         * Groupのローカル座標でのPickのテスト1
         * 何もないのでヒットしない
         * */
        [Fact]
        public void TestPickGroup1 () {
            var grp = new Group ();
            var ri  = new RayIntersection ();

            Assert.Equal(false, grp.Pick(0xffffffff, 0, 0, 0,  0, 0, -1, ri));

            grp.RemoveChild (msh);

        }

        /**
         * Groupのローカル座標でのPickのテスト2
         * バウンディングスフィアによって除外されることを確認するテスト
         * */
        [Fact]
        public void TestPickGroup2 () {
            var grp = new Group ();
            var ri  = new RayIntersection ();
            grp.AddChild (msh); 
            
            // 手前から奥
            Assert.Equal (false, grp.Pick (0xffffffff,  1,  1, 1,  0, 0, -1, ri));
            Assert.Equal (false, grp.Pick (0xffffffff,  1, -1, 1,  0, 0, -1, ri));
            Assert.Equal (false, grp.Pick (0xffffffff, -1,  1, 1,  0, 0, -1, ri));
            Assert.Equal (false, grp.Pick (0xffffffff, -1, -1, 1,  0, 0, -1, ri));
            // 上から下
            Assert.Equal (false, grp.Pick (0xffffffff,  1, 1,  1,  0, -1, 0, ri));
            Assert.Equal (false, grp.Pick (0xffffffff,  1, 1, -1,  0, -1, 0, ri));
            Assert.Equal (false, grp.Pick (0xffffffff, -1, 1,  1,  0, -1, 0, ri));
            Assert.Equal (false, grp.Pick (0xffffffff, -1, 1, -1,  0, -1, 0, ri));

            grp.RemoveChild (msh);

        }

        /**
         * Groupのローカル座標でのPickのテスト3
         * バウンディングボックスによって除外されることを確認するテスト
         * */
        [Fact]
        public void TestPickGroup3 () {
            var grp = new Group ();
            var ri  = new RayIntersection ();
            grp.AddChild (msh);

            // 手前から奥
            Assert.Equal (false, grp.Pick (0xffffffff, 2, 2, 2, 0, 0, -2, ri));
            Assert.Equal (false, grp.Pick (0xffffffff, 2, -2, 2, 0, 0, -2, ri));
            Assert.Equal (false, grp.Pick (0xffffffff, -2, 2, 2, 0, 0, -2, ri));
            Assert.Equal (false, grp.Pick (0xffffffff, -2, -2, 2, 0, 0, -2, ri));
            // 上から下
            Assert.Equal (false, grp.Pick (0xffffffff, 2, 2, 2, 0, -2, 0, ri));
            Assert.Equal (false, grp.Pick (0xffffffff, 2, 2, -2, 0, -2, 0, ri));
            Assert.Equal (false, grp.Pick (0xffffffff, -2, 2, 2, 0, -2, 0, ri));
            Assert.Equal (false, grp.Pick (0xffffffff, -2, 2, -2, 0, -2, 0, ri));

            grp.RemoveChild (msh);

        }



        /**
         * Groupのローカル座標でのPickのテスト4
         * メッシュにヒットしてRayIntersectionに適切な値が入ることを確認するテスト
         * */
        [Fact]
        public void TestPickGroup4 () {
            var grp = new Group ();
            var ri  = new RayIntersection ();
            grp.AddChild (msh);

            var expectedRay = new float[] {0,0,10, 0,0,-1}; 
            var expectedNormal = new float[] {0,0,1};
            var expectedTexCoords1 = new float[] { 0.5f, 0.5f };
            var expectedTexCoords2 = new float[] { 1.0f, 1.0f };

            Assert.Equal (true, grp.Pick (0xffffffff, 0, 0, 10, 0, 0, -1, ri));
            Assert.Equal (msh, ri.Intersected);
            Assert.Equal (10.0f, ri.Distance, new FloatComparere (0.00001f));
            Assert.Equal (expectedRay, ri.Ray);
            Assert.Equal (expectedNormal, ri.Normal);
            Assert.Equal (expectedTexCoords1, ri.Texture[0]);
            Assert.Equal (expectedTexCoords2, ri.Texture[1]);

            grp.RemoveChild (msh);
        }

        /**
         * Groupのローカル座標でのPickのテスト5
         * Rayが半直線で原点より後方にヒットしない事を確認するテスト
         * */
        [Fact]
        public void TestPickGroup5 () {
            var grp = new Group ();
            var ri  = new RayIntersection ();
            grp.AddChild (msh);

            Assert.Equal (false, grp.Pick (0xffffffff, 0, 0, -1, 0, 0, -1, ri));

            grp.RemoveChild (msh);

        }


        /**
         * Groupのローカル座標でのPickのテスト6
         * Pickがシーン階層をトラバースしその座標変換を反映している事を確認するテスト
         * */
        [Fact]
        public void TestPickGroup6 () {
            var grp1 = new Group ();
            var grp2 = new Group ();
            var grp3 = new Group ();
            var ri   = new RayIntersection ();
            grp1.AddChild (grp2);
            grp2.AddChild (grp3);
            grp3.AddChild (msh);
            grp1.Translate (0, 0, 0);
            grp2.Translate (100, 0, 0);
            grp3.Translate (0, 100, 0);

            Assert.Equal (true, grp1.Pick (0xffffffff, 100, 100, 10, 0, 0, -1, ri));
            Assert.Equal (grp3, ri.Intersected);
            Assert.Equal (9.0f, ri.Distance);

            grp3.RemoveChild (msh);
        }

        /**
         * Groupのローカル座標でのPickのテスト7
         * PickがSkinMeshをまたいでボーン階層の方にもトラバースする事を確認するテスト
         * grp --> sknMsh --> skl --> msh
         * */
        [Fact]
        public void TestPickGroup7 () {
            var grp = new Group ();
            var skl = new Group ();
            var ri = new RayIntersection ();
            var skinMesh = new SkinnedMesh (1, 0, skl);
            grp.AddChild (skinMesh);
            skl.AddChild (msh);
            skl.Translate (100, 0, -1);

            Assert.Equal (true, grp.Pick (0xffffffff, 100, 0, 10, 0, 0, -1, ri));
            Assert.Equal (skl, ri.Intersected);
            Assert.Equal (10.0f, ri.Distance);

            grp.RemoveChild (msh);
        }

    }
}
